package com.px.project.module.ml.service.impl;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.px.common.config.exception.BusinessException;
import com.px.common.config.security.LoginContextHolder;
import com.px.common.entity.FileInfo;
import com.px.common.service.FileInfoService;
import com.px.project.module.ml.constant.FileTypeConstant;
import com.px.project.module.ml.dto.storage.StorageDetailDto;
import com.px.project.module.ml.dto.storage.StorageDto;
import com.px.project.module.ml.entity.Stock;
import com.px.project.module.ml.entity.StockDetail;
import com.px.project.module.ml.entity.StockInventory;
import com.px.project.module.ml.entity.StorageDetail;
import com.px.project.module.ml.enums.StorageExceptionEnum;
import com.px.project.module.ml.mapper.StorageDetailMapper;
import com.px.project.module.ml.service.IStockDetailService;
import com.px.project.module.ml.service.IStockInventoryService;
import com.px.project.module.ml.service.IStockService;
import com.px.project.module.ml.service.IStorageDetailService;
import com.px.project.module.ml.vo.stock.StockInventoryVo;
import com.px.project.module.ml.vo.storage.StorageDetailVo;
import com.px.project.module.ml.vo.storage.StorageVo;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;

/**
 * 入库单明细 服务实现类
 *
 * @author 品讯科技
 * @since 2023-11-29
 */
@Service
@RequiredArgsConstructor
public class StorageDetailServiceImpl extends ServiceImpl<StorageDetailMapper, StorageDetail> implements IStorageDetailService {
    private static final String DEFAULT_BIGDECIMAL = "0.00";

    private final IStockService stockService;

    private final IStockDetailService stockDetailService;

    private final IStockInventoryService stockInventoryService;

    private final StorageDetailMapper storageDetailMapper;

    private final FileInfoService fileInfoService;

    @Override
    public List<StorageDetailVo> getStorageDetailVo(Long storageId) {
        return baseMapper.getStorageDetailVo(storageId, FileTypeConstant.STORAGE_DETAIL_QUALITY);
    }

    @Override
    public boolean saveStorageDetail(StorageDto dto,Boolean isDraft) {
        List<StorageDetailDto> detailList = dto.getDetailList();
        if(CollUtil.isEmpty(detailList)){
            return false;
        }
        checkStorageDetail(detailList,isDraft);
        detailList.forEach(detailDto -> {
            StorageDetail storageDetail = new StorageDetail();
            BeanUtils.copyProperties(detailDto,storageDetail);

            storageDetail.setStorageId(dto.getId());
            super.save(storageDetail);

            List<Long> fileIdList = detailDto.getFileIdList();
            if(CollUtil.isNotEmpty(fileIdList)){
                fileIdList.forEach(fileId -> fileInfoService.update(Wrappers.lambdaUpdate(FileInfo.class)
                        .set(FileInfo::getBiId,storageDetail.getId())
                        .eq(FileInfo::getId,fileId)));
            }
        });
        return Boolean.TRUE;
    }

    private void checkStorageDetail(List<StorageDetailDto> detailList,Boolean isDraft){
        if(Boolean.TRUE.equals(isDraft)){
            return;
        }
        //同一入库单下材料唯一
        detailList.stream().collect(Collectors.groupingBy(StorageDetailDto::getModel,Collectors.counting()))
                .forEach((model,value) -> {
                    if(value > 1L){
                        throw new BusinessException(StorageExceptionEnum.REPEAT_MATERIAL,model);
                    }
                });
    }

    @Override
    public boolean updateStorageDetail(StorageDto dto,Boolean isDraft) {
        List<StorageDetailDto> detailList = dto.getDetailList();
        if(CollUtil.isEmpty(detailList)){
            return false;
        }
        checkStorageDetail(detailList, isDraft);
        //way one
        List<Long> oldIdList = list(Wrappers.lambdaQuery(StorageDetail.class).eq(StorageDetail::getStorageId,dto.getId()))
                .stream().map(StorageDetail::getId).collect(Collectors.toList());
        Set<Long> updateIds = new HashSet<>();
        detailList.forEach(storageDetail -> {
            if(Objects.isNull(storageDetail.getId())){
                storageDetail.setStorageId(dto.getId());

                List<Long> fileIdList = storageDetail.getFileIdList();
                if(CollUtil.isNotEmpty(fileIdList)){
                    fileIdList.forEach(fileId -> fileInfoService.update(Wrappers.lambdaUpdate(FileInfo.class)
                            .set(FileInfo::getBiId,storageDetail.getId())
                            .eq(FileInfo::getId,fileId)));
                }

                super.save(storageDetail);
            }else{
                updateIds.add(storageDetail.getId());

                List<Long> fileIdList = storageDetail.getFileIdList();
                if(CollUtil.isNotEmpty(fileIdList)){
                    fileIdList.forEach(fileId -> fileInfoService.update(Wrappers.lambdaUpdate(FileInfo.class)
                            .set(FileInfo::getBiId,storageDetail.getId())
                            .eq(FileInfo::getId,fileId)));
                }

                super.updateById(storageDetail);
            }
        });
        oldIdList.removeIf(updateIds::contains);
        if(CollUtil.isNotEmpty(oldIdList)){
            super.removeByIds(oldIdList);
        }
        return Boolean.TRUE;
    }

    @Override
    public boolean updateStock(StorageVo storageVo) {
        List<StorageDetailVo> storageDetailVoList = storageVo.getDetailList();
        if(CollUtil.isEmpty(storageDetailVoList)){
            return false;
        }
        Map<Long,Stock> stockMap = stockService.list(Wrappers.lambdaQuery(Stock.class).eq(Stock::getStoreId,storageVo.getStoreId()))
                        .stream().collect(Collectors.toMap(Stock::getMaterialId, Function.identity()));

        String currentMonth = LocalDate.now().format(DatePattern.NORM_MONTH_FORMATTER);
        Map<Long,StockInventory> inventoryMap = stockInventoryService.list(Wrappers.lambdaQuery(StockInventory.class).eq(StockInventory::getInventoryDate,currentMonth))
                .stream().collect(Collectors.toMap(StockInventory::getMaterialId, Function.identity()));

        List<StockDetail> stockDetailList = new LinkedList<>();
        storageDetailVoList.forEach(storageDetailVo -> {
            Long materialId = storageDetailVo.getMaterialId();
            Stock stock;
            if(stockMap.containsKey(materialId)){
                stock = stockMap.get(materialId);

                stockService.update(Wrappers.lambdaUpdate(Stock.class)
                        .set(Stock::getTotalStorage,stock.getTotalStorage().add(storageDetailVo.getNum()))
                        .set(Stock::getLeaveNum,stock.getLeaveNum().add(storageDetailVo.getNum()))
                        .eq(Stock::getId,stock.getId()));
            }else{
                stock = new Stock();
                stock.setStoreId(storageVo.getStoreId());
                stock.setTypeId(storageVo.getTypeId());
                stock.setMaterialId(materialId);
                stock.setTotalStorage(storageDetailVo.getNum());
                stock.setLeaveNum(storageDetailVo.getNum());
                stock.setUpdateBy(LoginContextHolder.me().getId());
                stock.setUpdateTime(LocalDateTime.now());
                stockService.save(stock);
            }
            StockDetail stockDetail = new StockDetail();
            stockDetail.setStockId(stock.getId());
            stockDetail.setStorageId(storageVo.getId());
            stockDetail.setMaterialId(materialId);
            stockDetail.setTotalStorage(storageDetailVo.getNum());
            stockDetail.setLeaveNum(storageDetailVo.getNum());

            stockDetailList.add(stockDetail);
            StockInventory inventory;
            if(inventoryMap.containsKey(materialId)){
                inventory = inventoryMap.get(materialId);

                inventory.setLeaveNum(inventory.getLeaveNum().add(storageDetailVo.getNum()));
                stockInventoryService.update(Wrappers.lambdaUpdate(StockInventory.class)
                        .set(StockInventory::getLeaveNum,inventory.getLeaveNum())
                        .eq(StockInventory::getId,inventory.getId()));
            }else{
                inventory = new StockInventory();

                inventory.setInventoryDate(currentMonth);
                inventory.setMaterialId(materialId);
                inventory.setLeaveNum(storageDetailVo.getNum());

                stockInventoryService.save(inventory);
            }
        });

        return stockDetailService.saveBatch(stockDetailList);
    }

    @Override
    public BigDecimal findSpendingPurchase(Long materialId, Long modelId, DateTime lastMonthStartDate, DateTime lastMonthEndDate) {
        return this.storageDetailMapper.selectSpendingPurchase(materialId, modelId, lastMonthStartDate, lastMonthEndDate);
    }

    @Override
    public BigDecimal findAggregatePurchase(Long materialId, Long modelId, DateTime lastMonthDate) {
        return this.storageDetailMapper.selectAggregatePurchase(materialId, modelId, lastMonthDate);
    }

    @Override
    public BigDecimal findSpendingInventory(Long materialId, Long modelId, Date verificationDate) {
        DateTime lastMonthDate = DateUtil.offsetMonth(verificationDate, -2);
        Integer year = lastMonthDate.year();
        Integer month = lastMonthDate.month() + 1;
        /* 查询上月是否最新盘点 */
        StockInventoryVo stockInventory = this.storageDetailMapper.selectSpendingInventory(materialId, modelId, year, month);
        if (ObjectUtil.isNotEmpty(stockInventory)) {
            return stockInventory.getLeaveNum();
        }
        /* 查询历史数据中最新盘点 */
        StockInventoryVo stockInventoryOfOld = this.storageDetailMapper.selectSpendingInventoryOfOld(materialId, modelId, year, month);
        if (ObjectUtil.isNotEmpty(stockInventory)) {
            return stockInventoryOfOld.getLeaveNum();
        }
        return new BigDecimal(DEFAULT_BIGDECIMAL);
    }

    @Override
    public BigDecimal findSpendingStock(Long materialId, Long modelId, Integer year, Integer month) {
        /* 查询上月是否最新盘点 */
        StockInventoryVo stockInventory = this.storageDetailMapper.selectSpendingInventory(materialId, modelId, year, month);
        if (ObjectUtil.isNotEmpty(stockInventory)) {
            return stockInventory.getLeaveNum();
        }
        /* 查询历史数据中最新盘点 */
        StockInventoryVo stockInventoryOfOld = this.storageDetailMapper.selectSpendingInventoryOfOld(materialId, modelId, year, month);
        if (ObjectUtil.isNotEmpty(stockInventory)) {
            return stockInventoryOfOld.getLeaveNum();
        }
        return new BigDecimal(DEFAULT_BIGDECIMAL);
    }

    @Override
    public Integer getStorageCount(DateTime beginDate, DateTime endDate) {
        return this.storageDetailMapper.selectStorageCount(beginDate,endDate);
    }
}
